#include "player_manager.h"
#include "dfx/error/av_codec_sample_error.h"
#include "plugin_manager.h"
#include <cstdint>
#include <future>

#undef LOG_TAG
#define LOG_TAG "PLAYERMANAGER"

namespace {
struct AsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    napi_deferred deferred;

    int32_t resultCode = 0;
    std::string resultStr = "";
    SampleInfo sampleInfo;
};

void DealCallBack(napi_env env, void *data) {
    AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);

    napi_value videoWidth;
    napi_create_int32(env, asyncCallbackInfo->sampleInfo.videoWidth, &videoWidth);
    napi_value videoHeight;
    napi_create_int32(env, asyncCallbackInfo->sampleInfo.videoHeight, &videoHeight);
    napi_value durationTime;
    napi_create_int64(env, asyncCallbackInfo->sampleInfo.durationTime, &durationTime);
    napi_value code;
    napi_create_int32(env, asyncCallbackInfo->resultCode, &code);
    napi_value message;
    napi_create_string_utf8(env, asyncCallbackInfo->resultStr.data(), NAPI_AUTO_LENGTH, &message);
    napi_value obj;
    napi_create_object(env, &obj);

    napi_set_named_property(env, obj, "code", code);
    napi_set_named_property(env, obj, "message", message);
    napi_set_named_property(env, obj, "videoWidth", videoWidth);
    napi_set_named_property(env, obj, "videoHeight", videoHeight);
    napi_set_named_property(env, obj, "durationTime", durationTime);
    napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, obj);
    napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
    delete asyncCallbackInfo;
}

void SetCallBackResult(AsyncCallbackInfo *asyncCallbackInfo, int32_t code, std::string str) {
    asyncCallbackInfo->resultCode = code;
    asyncCallbackInfo->resultStr = str;
}

void SetVideoInfo(AsyncCallbackInfo *asyncCallbackInfo, int32_t code, std::string str, SampleInfo sampleInfo) {
    asyncCallbackInfo->resultCode = code;
    asyncCallbackInfo->resultStr = str;
    asyncCallbackInfo->sampleInfo.videoWidth = sampleInfo.videoWidth;
    asyncCallbackInfo->sampleInfo.videoHeight = sampleInfo.videoHeight;
    asyncCallbackInfo->sampleInfo.durationTime = sampleInfo.durationTime;
}

void Swipey(napi_env env, void *data) {
    AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
    AVCODEC_SAMPLE_LOGI("before swipeeeeeeeeeeeeeeeeeeeeeeee");
    std::future<void> future = std::async(std::launch::async, [&asyncCallbackInfo]() {
        PlayerManager::getInstance().Swipe(asyncCallbackInfo->sampleInfo);
    });
    future.wait();
    AVCODEC_SAMPLE_LOGI("videoWidth is %{public}i,videoHeight is %{public}i",asyncCallbackInfo->sampleInfo.videoWidth, asyncCallbackInfo->sampleInfo.videoHeight);
    SetVideoInfo(asyncCallbackInfo, 0, "Success", asyncCallbackInfo->sampleInfo);
}

void NativeStart(napi_env env, void *data) {
    AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
    int32_t ret = PlayerManager::getInstance().Start();
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        SetCallBackResult(asyncCallbackInfo, -1, "Player init failed");
    } else {
        SetCallBackResult(asyncCallbackInfo, 0, "Player start finish");
    }
}
} // namespace

napi_value PlayerManager::Swipe(napi_env env, napi_callback_info info) {
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    size_t argc = 5;
    napi_value args[5] = {nullptr};
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), nullptr,
        "GetContext napi_get_cb_info failed");
    SampleInfo sampleInfo;
    int index = 0;
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int32(env, args[index++], &sampleInfo.videoId), nullptr,
        "Initialize: Get value failed");
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int32(env, args[index++], &sampleInfo.inputFd), nullptr,
        "Initialize: Get value failed");
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int64(env, args[index++], &sampleInfo.inputFileOffset), nullptr,
        "Initialize: Get value failed");
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int64(env, args[index++], &sampleInfo.inputFileSize), nullptr,
        "Initialize: Get value failed");
    bool lossless = true;
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_bigint_int64(env, args[index++],
        &sampleInfo.surfaceId, &lossless), nullptr, "Initialize: Get value failed");
    sampleInfo.window = PluginManager::getInstance().windowMap_.at(sampleInfo.surfaceId);
    
    napi_value promise;
    napi_deferred deferred;
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_promise(env, &deferred, &promise), nullptr,
        "Initialize: Create promise failed");
    AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo();
    asyncCallbackInfo->env = env;
    asyncCallbackInfo->asyncWork = nullptr;
    asyncCallbackInfo->deferred = deferred;
    asyncCallbackInfo->sampleInfo = sampleInfo;
    napi_value resourceName;
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_string_latin1(env, "Initialize", NAPI_AUTO_LENGTH, &resourceName),
        nullptr, "Initialize: Create resourceName failed");
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_async_work(
        env, nullptr, resourceName, [](napi_env env, void *data) { Swipey(env, data); },
        [](napi_env env, napi_status status, void *data) { DealCallBack(env, data); },
        (void *)asyncCallbackInfo, &asyncCallbackInfo->asyncWork), nullptr, "Initialize: Create async work failed");
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_queue_async_work(env, asyncCallbackInfo->asyncWork),
        nullptr, "Initialize: queue_async_work failed");
    return promise;
}

napi_value PlayerManager::Start(napi_env env, napi_callback_info info)
{
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    napi_deferred deferred;
    napi_value promise;
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_promise(env, &deferred, &promise), nullptr,
        "Start: Create promise failed");
    AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo();
    asyncCallbackInfo->env = env;
    asyncCallbackInfo->asyncWork = nullptr;
    asyncCallbackInfo->deferred = deferred;
    napi_value resourceName;
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_string_latin1(env, "Start", NAPI_AUTO_LENGTH, &resourceName),
        nullptr, "Start: Create resourceName failed");
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_async_work(
        env, nullptr, resourceName, [](napi_env env, void *data) { NativeStart(env, data); },
        [](napi_env env, napi_status status, void *data) { DealCallBack(env, data); },
        (void *)asyncCallbackInfo, &asyncCallbackInfo->asyncWork), nullptr, "Initialize: Create async work failed");
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_queue_async_work(env, asyncCallbackInfo->asyncWork),
        nullptr, "Start: queue_async_work failed");
    return promise;
}

napi_value PlayerManager::Pause(napi_env env, napi_callback_info info)
{
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    int32_t ret = PlayerManager::getInstance().Pause();
    CHECK_AND_RETURN_RET_LOG(AVCODEC_SAMPLE_ERR_OK == ret, nullptr, "Pause failed");
    return nullptr;
}

napi_value PlayerManager::Resume(napi_env env, napi_callback_info info)
{
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    int32_t ret = PlayerManager::getInstance().Resume();
    CHECK_AND_RETURN_RET_LOG(AVCODEC_SAMPLE_ERR_OK == ret, nullptr, "Resume failed");
    return nullptr;
}

napi_value PlayerManager::Seek(napi_env env, napi_callback_info info)
{
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), nullptr,
        "GetContext napi_get_cb_info failed");
    int64_t position = 0;
    CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int64(env, args[0], &position), nullptr,
        "Seek: Get value failed");
    int32_t ret = PlayerManager::getInstance().Seek(position);
    CHECK_AND_RETURN_RET_LOG(AVCODEC_SAMPLE_ERR_OK == ret, nullptr, "Seek failed");
    return nullptr;
}

napi_value PlayerManager::Kill(napi_env env, napi_callback_info info)
{
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    PlayerManager::getInstance().Kill();
    return nullptr;
}



